#include "stdafx.h"
#include "cppflibinc.h"

#include "MyEnumDecl.h"

using namespace cppflib;
using namespace cppflib::os;
using namespace cppflib::lang;

static void TestPrimitiveTypes()
{
   Int32 a(13);
   Int32 b(13);
   
   assert(a.Equals(b));
   assert(a.Value() == 13);
   assert(a.GetHash() == 13);
   a.Set(14);
   assert(!a.Equals(b));

   WChar h(_S('q'));
   WChar h2(_S('p'));

   assert(!h.Equals(h2));
   assert(h.GetHash() == 113);
   h.Set(_S('p'));
   assert(h.Equals(h2));
   

   Int64 e(I64N(4611686018427387904));
   Int64 e1(21213343);
   assert(!e.Equals(e1));
   assert(e.GetHash() == 0x40000000); // may have error if compiled on big endian machine

   assert(!e.Equals(h));
   
   Double d(10.323);
   Double d1(10.323);
   assert(d.Equals(d1));
}

void TestEnumDeclaration()
{
   cppmempool::MemPoolWrap memPool;

   ptr_t addr1 = reinterpret_cast<ptr_t>(Enum::GetEnumTable<DAYS>());
   ptr_t addr2 = reinterpret_cast<ptr_t>(Enum::GetEnumTable<DAYS>());
   assert(addr1 == addr2);

   // test DAYS
   assert(Enum::IsDefined<DAYS>(Sunday));
   assert(!Enum::IsDefined<DAYS>((DAYS)10));
   
   assert(Enum::Parse<DAYS>(_S("Wednesday")) == (DAYS)4);
   assert(Enum::Parse<DAYS>(_S("Friday")) == (DAYS)6);

   DAYS dVal = (DAYS)5;
   assert(CFString::WStrcmp(Enum::GetString(dVal), _S("Thursday")) == 0);
   assert(CFString::WStrcmp(Enum::GetString(Monday), _S("Monday")) == 0);

   assert(Enum::GetTotalEnums<DAYS>() == 6);

   try {
      Enum::Parse<DAYS>(_S("Saturday"));
   }
   catch (exceptions::ArgumentException &exp) {
      debug::Print(debug::DL_DEBUG, _S("err = %s\r\n"), exp.GetMsg());
   }

   try {
      Enum::GetString<DAYS>((DAYS)7);
   }
   catch (exceptions::ArgumentException &exp) {
      debug::Print(debug::DL_DEBUG, _S("err = %s\r\n"), exp.GetMsg());
   }

   // test MONTHS
   assert(Enum::IsDefined<hello::world::MONTHS>(hello::world::Jan));
   assert(Enum::IsDefined<hello::world::MONTHS>(hello::world::Feb));
   assert(!Enum::IsDefined<hello::world::MONTHS>((hello::world::MONTHS)20));
   
   assert(Enum::Parse<hello::world::MONTHS>(_S("Apr")) == (hello::world::MONTHS)11);
   assert(Enum::Parse<hello::world::MONTHS>(_S("Feb")) == (hello::world::MONTHS)2);

   assert(CFString::WStrcmp(Enum::GetString(hello::world::Jun), _S("Jun")) == 0);
   assert(CFString::WStrcmp(Enum::GetString(hello::world::May), _S("May")) == 0);

   assert(Enum::GetTotalEnums<hello::world::MONTHS>() == 6);
}

void TestIntOp(void)
{
   // 32 bit signed
   assert(!lang::CheckOverflow(lang::AOP_ADD, static_cast<int>(2147483644), static_cast<int>(2)));
   assert(!lang::CheckOverflow(lang::AOP_MUL, static_cast<int>(21474836), static_cast<int>(3)));
   assert(!lang::CheckOverflow(lang::AOP_MUL, static_cast<int>(2147483647), static_cast<int>(-1)));

   assert(lang::CheckOverflow(lang::AOP_ADD, static_cast<int>(2147483647), static_cast<int>(1)));
   assert(lang::CheckOverflow(lang::AOP_ADD, static_cast<int>(2147483647), static_cast<int>(2)));
   assert(lang::CheckOverflow(lang::AOP_ADD, static_cast<int>(-2147483647), static_cast<int>(-2)));

   assert(lang::CheckOverflow(lang::AOP_SUB, static_cast<int>(22222), static_cast<int>(-2147483647)));
   assert(lang::CheckOverflow(lang::AOP_SUB, static_cast<int>(0x80000000), static_cast<int>(2)));
   assert(lang::CheckOverflow(lang::AOP_SUB, static_cast<int>(-2147483642), static_cast<int>(11111)));

   assert(lang::CheckOverflow(lang::AOP_MUL, static_cast<int>(2147483647), static_cast<int>(2)));
   assert(lang::CheckOverflow(lang::AOP_MUL, static_cast<int>(-2147483645), static_cast<int>(-2147483641)));
   assert(lang::CheckOverflow(lang::AOP_MUL, static_cast<int>(-268435456), static_cast<int>(10)));
   assert(lang::CheckOverflow(lang::AOP_MUL, static_cast<int>(268435456), static_cast<int>(-10)));
   assert(lang::CheckOverflow(lang::AOP_MUL, static_cast<int>(0x80000000), static_cast<int>(-1)));

   // 32 bit unsigned
   assert(!lang::CheckOverflow(lang::AOP_ADD, static_cast<u32_t>(2147483641), static_cast<u32_t>(2147483642)));
   assert(!lang::CheckOverflow(lang::AOP_MUL, static_cast<u32_t>(2147483647), static_cast<u32_t>(2)));

   assert(lang::CheckOverflow(lang::AOP_ADD, static_cast<u32_t>(0x80000001), static_cast<u32_t>(0x80000060)));
   assert(lang::CheckOverflow(lang::AOP_ADD, static_cast<u32_t>(0xFFFFFFFE), static_cast<u32_t>(2)));
   assert(lang::CheckOverflow(lang::AOP_SUB, static_cast<u32_t>(2147483647), static_cast<u32_t>(0x80000001)));
   assert(lang::CheckOverflow(lang::AOP_MUL, static_cast<u32_t>(0x80000001), static_cast<u32_t>(0x80000001)));
   assert(lang::CheckOverflow(lang::AOP_MUL, static_cast<u32_t>(0xFFFFFFFF), static_cast<u32_t>(0xFFFFFFFF)));
   assert(lang::CheckOverflow(lang::AOP_MUL, static_cast<u32_t>(858993459), static_cast<u32_t>(6)));

   // 64 bit signed
   assert(!lang::CheckOverflow(lang::AOP_ADD, static_cast<i64_t>(I64N(0x7ffffffffffffffe)), static_cast<i64_t>(1)));
   assert(!lang::CheckOverflow(lang::AOP_MUL, static_cast<i64_t>(I64N(0x7ffffffffffffff)), static_cast<i64_t>(2)));
   assert(!lang::CheckOverflow(lang::AOP_MUL, static_cast<i64_t>(I64N(0x7ffffffffffffff)), static_cast<i64_t>(-1)));

   assert(lang::CheckOverflow(lang::AOP_ADD, static_cast<i64_t>(I64N(0x7fffffffffffffff)), static_cast<i64_t>(1)));
   assert(lang::CheckOverflow(lang::AOP_ADD, static_cast<i64_t>(I64N(0x7ffffffffffffffe)), static_cast<i64_t>(2)));
   assert(lang::CheckOverflow(lang::AOP_ADD, static_cast<i64_t>(I64N(0x8000000000000001)), static_cast<i64_t>(-2)));

   assert(lang::CheckOverflow(lang::AOP_SUB, static_cast<i64_t>(I64N(0x7fffffffffffff11)), static_cast<i64_t>(-938333)));
   assert(lang::CheckOverflow(lang::AOP_SUB, static_cast<i64_t>(I64N(0x8000000000000000)), static_cast<i64_t>(1)));
   assert(lang::CheckOverflow(lang::AOP_SUB, static_cast<i64_t>(I64N(9223372036854775800)), static_cast<i64_t>(-2222)));

   assert(lang::CheckOverflow(lang::AOP_MUL, static_cast<i64_t>(I64N(0x8000000000000000)), static_cast<i64_t>(-1)));
   assert(lang::CheckOverflow(lang::AOP_MUL, static_cast<i64_t>(I64N(8589934592)), static_cast<i64_t>(I64N(8589934592))));
   assert(lang::CheckOverflow(lang::AOP_MUL, static_cast<i64_t>(I64N(-8589934592)), static_cast<i64_t>(I64N(-8589934592))));
   assert(lang::CheckOverflow(lang::AOP_MUL, static_cast<i64_t>(I64N(-1152921504606846976)), static_cast<i64_t>(I64N(9))));
   assert(lang::CheckOverflow(lang::AOP_MUL, static_cast<i64_t>(I64N(9)), static_cast<i64_t>(I64N(-1152921504606846976))));

   // 64 bit unsigned
   assert(!lang::CheckOverflow(lang::AOP_ADD, static_cast<u64_t>(U64N(0x7ffffffffffffffe)), static_cast<u64_t>(U64N(0x7ffffffffffffffe))));
   assert(!lang::CheckOverflow(lang::AOP_MUL, static_cast<u64_t>(U64N(0x7ffffffffffffffe)), static_cast<u64_t>(2)));

   assert(lang::CheckOverflow(lang::AOP_ADD, static_cast<u64_t>(U64N(0x8000000000000000)), static_cast<u64_t>(U64N(0x8000000000000000))));
   assert(lang::CheckOverflow(lang::AOP_ADD, static_cast<u64_t>(U64N(0xffffffffffffffee)), static_cast<u64_t>(U64N(0xee))));
   assert(lang::CheckOverflow(lang::AOP_SUB, static_cast<u64_t>(U64N(0x8000000000000000)), static_cast<u64_t>(U64N(0x8000000000000001))));
   assert(lang::CheckOverflow(lang::AOP_MUL, static_cast<u64_t>(U64N(0x100000000)), static_cast<u64_t>(U64N(0x100000000))));
   assert(lang::CheckOverflow(lang::AOP_MUL, static_cast<u64_t>(U64N(0x8000000000000000)), static_cast<u64_t>(U64N(2))));
   assert(lang::CheckOverflow(lang::AOP_MUL, static_cast<u64_t>(U64N(0xffffffffffffffee)), static_cast<u64_t>(U64N(2))));

}

static void TestCheckNumType()
{
   
   assert(lang::CheckNumType(_S("2832")) == lang::NUMT_SINT32);
   assert(lang::CheckNumType(_S("2147483647")) == lang::NUMT_SINT32);
   assert(lang::CheckNumType(_S("-2147483648")) == lang::NUMT_SINT32);
   assert(lang::CheckNumType(_S("2147483648")) == lang::NUMT_SINT64);
   assert(lang::CheckNumType(_S("-2147483649")) == lang::NUMT_SINT64);
   assert(lang::CheckNumType(_S("9223372036854775807")) == lang::NUMT_SINT64);
   assert(lang::CheckNumType(_S("9223372036854775808")) == lang::NUMT_SINT64OVERFLOW);
   assert(lang::CheckNumType(_S("0")) == lang::NUMT_SINT32);   
   assert(lang::CheckNumType(_S("-0")) == lang::NUMT_SINT32);   
   assert(lang::CheckNumType(_S("2")) == lang::NUMT_SINT32);   
   assert(lang::CheckNumType(_S("-9")) == lang::NUMT_SINT32);   

   assert(lang::CheckNumType(_S("33.333")) == lang::NUMT_DOUBLE);
   assert(lang::CheckNumType(_S("-2.00")) == lang::NUMT_DOUBLE);
   assert(lang::CheckNumType(_S("0.01")) == lang::NUMT_DOUBLE);
   assert(lang::CheckNumType(_S("0.3e2")) == lang::NUMT_DOUBLE);
   assert(lang::CheckNumType(_S("13.0e2")) == lang::NUMT_DOUBLE);
   assert(lang::CheckNumType(_S("13e2")) == lang::NUMT_DOUBLE);
   assert(lang::CheckNumType(_S("-0e-2")) == lang::NUMT_DOUBLE);
   assert(lang::CheckNumType(_S("0e12")) == lang::NUMT_DOUBLE);
   assert(lang::CheckNumType(_S("0e+2")) == lang::NUMT_DOUBLE);
   assert(lang::CheckNumType(_S("-0.23e+2")) == lang::NUMT_DOUBLE);
   assert(lang::CheckNumType(_S("-54.44E+2")) == lang::NUMT_DOUBLE);
   
   assert(lang::CheckNumType(_S("01.44")) == lang::NUMT_UNKNOWN);  // dp must come after '0'
   assert(lang::CheckNumType(_S("1.44e")) == lang::NUMT_UNKNOWN);
   assert(lang::CheckNumType(_S("1.e23")) == lang::NUMT_UNKNOWN);
   assert(lang::CheckNumType(_S("1.")) == lang::NUMT_UNKNOWN);
   assert(lang::CheckNumType(_S("1.0e-")) == lang::NUMT_UNKNOWN);
   assert(lang::CheckNumType(_S("1.0e+")) == lang::NUMT_UNKNOWN);
   assert(lang::CheckNumType(_S("1a.9e3")) == lang::NUMT_UNKNOWN);
   assert(lang::CheckNumType(_S("1.1ae4")) == lang::NUMT_UNKNOWN);
   assert(lang::CheckNumType(_S("1.a1e4")) == lang::NUMT_UNKNOWN);
   assert(lang::CheckNumType(_S("1.9ea")) == lang::NUMT_UNKNOWN);
   assert(lang::CheckNumType(_S("1.9a")) == lang::NUMT_UNKNOWN);
   assert(lang::CheckNumType(_S("-a1.9")) == lang::NUMT_UNKNOWN);
   assert(lang::CheckNumType(_S("-1a.9")) == lang::NUMT_UNKNOWN);
   assert(lang::CheckNumType(_S("-0a.9")) == lang::NUMT_UNKNOWN);
   assert(lang::CheckNumType(_S("-a0.9")) == lang::NUMT_UNKNOWN);
}

void TestLang(void)
{
   debug::Print(_S("Testing lang ...\n"));
   TestPrimitiveTypes();
   TestEnumDeclaration();
   TestEnumDeclaration();
   TestIntOp();
   TestCheckNumType();
}
